#ifndef _imt_client_h_
#define _imt_client_h_

#include "imtPublic.h"
#include "imtNetMsgQueue.h"
#include "imtSocket.h"
#include "imtThread.h"
#include "imtLock.h"

#include "OgreTimer.h"

namespace iMate {

	class Socket;

	///temp buffer init size
	static const int MSG_TEMP_UFFER_INIT_SIZE = 128;
	static const size_t MsgHeaderSize = 4;
	static const size_t CmdMaxSize = 1024 * 32;
	static const size_t MsgMaxSize = CmdMaxSize + MsgHeaderSize;

	///error code for message pop
	enum MessageError
	{
		ME_DataEmpty = 0,
		ME_DataUncomplete = -1,
		ME_DataTooBig = -2,
		ME_DataError = -3
	};

	/**a client tool for connecting to server
	*/
	class Client : public NormalAlloc
	{
	public:
		Client(): mThread(0), mLock(0),
		          mP(0), mV(0), 
		          mWaitTime(0)
		{/***/}

		~Client()
		{release();}

		/**set the server information for connecting
		@param server    server address: a numeric host address string 
		          @par dotted-decimal IPv4 address or an IPv6 hex address
	    @param port     a numeric string
		@return  success to true
		*/
		void setServerInfo(const String& server, const String& port)
		{
			mServer = server;
			mPort = port;
		}
		/**some init buffer size attributes for optimazation
		@Note just IGNORE it!
		@param intMsgBufSize init size of message queue buffer
		@param initTmpBufSize init size of temp buffer
		*/
		void setBufferAttrib(size_t intMsgBufSize =MSG_QUEUE_INIT_SIZE, size_t initTmpBufSize =MSG_TEMP_UFFER_INIT_SIZE)
		{
			//do nothing
		}

		/**@return if init success, return true*/
		bool init();
		///release everything
		void release();

		/**connect to server asigned by setServerInfo(@see setServerInfo)
		@return true if connection success
		*/
		bool connect();
		///disconnect from server
		void disconnect();

		/**get(and pop) a message from receive-message queue
		@buffer buffer to holde message
		@size the size of buffer
		@return the size of data poped
		   @par return ME_DataEmpty if message queue is empty
		        return ME_DataUncomplete if data for a message has not been received completely
				return ME_DataTooBig if the buffer passed in is not enough for current message
				return ME_DataError if the parameters are wrong
		*/
		int popMessage(void* buffer, size_t size);
		/**push a message to send-message queue
	    @param msg    a complete message
		@param size   the size of message
		@param toSelf   send it to server if false, otherwise push it to receive-message 
		     @par queue to simulating a message's arrival from server
		@return the size of data pushed
		   @par return ME_DataEmpty if message queue is empty
		        return ME_DataUncomplete if data for a message has not been received completely
				return ME_DataTooBig if the buffer passed in is not enough for current message
				return ME_DataError if the parameters are wrong
		*/
		int pushMessage(const void* msg, size_t size, bool toSelf = false);

		/**the main functin of message-receive thread
		@param thread the thread
		@param data the user data
		*/
		void _netMainRun(IThread* thread, UserData& data);

	protected:
		/**push a block of data to receive-message queue
		@Note the data pushed is no need to be a single-and-complete application-level message,
		   @par but may be a part of a complete message or composed of serveral messages.
	    @param data a bolock of data
		@param size the size of data
		@return a positive number if success, 
		*/
		int pushData(const void* data, size_t size);
		/**when 2 queues are empty, the v-operation thread should wait
		   @par some time and access message queues again. this function
		   @par is to calculate the time according to the message unprocessed number  
		@param the message unprocessed number
		*/
		void setWaitTime(size_t msgCount);

		/**encrypt one message in data
		@param data data may hold a sequence of messages
		@param size max size of data
		@return the size of the message decrypted if succeeded,  
		   @par DE_DataUncomplete if data is not enough for a single message
		        DE_DataError if no message decrypted
		*/
		int encrypt(char* data, size_t size);
		/**decrypt one message in data
		@param data data may hold a sequence of messages
		@param size max size of data
		@param msgHead return the begin of a message decrypted successfully
		@return the size of the message decrypted if succeeded,  
		   @par DE_DataUncomplete if data is not enough for a single message
		        DE_DataError if no message decrypted
		*/
		int decrypt(char* data, size_t size, char** msgHead);

	protected:
		///server ip address
		String mServer;
		///server process port
		String mPort;
		///socket for connecting
		Socket mSocket;
		///thread for receiving message(there's no sending thread)
		IThread* mThread;

		///temp buffer for raw data from server
		std::vector<char> mBuffer;
		char mSendBuffer[MsgMaxSize];

		///message queue A
		NetMsgQueue mMsgQueueA;
		///message queue B
		NetMsgQueue mMsgQueueB;
		///a ptr for message-P operation (pop)
		NetMsgQueue* mP;
		///a ptr for message-V operation (push)
		NetMsgQueue* mV;
		///locker for messag queue (just for mV)
		ILock* mLock;
		/**when 2 queues are empty, the v-operation thread should wait
		@par some time and access message queues again.
		*/
		ulong mWaitTime;
		///for wait time
		Timer mTimer;
	};

}//iMate









#endif //_imt_client_h_